﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Media;
using System.Windows;
using System.Windows.Media.Imaging;



namespace Yuv
{
  public enum YUVType
  {
    FOUR_TWO_ZERO,
    FOUR_ZERO_ZERO
  };

  public class YuvReader
  {    
    
    byte[] y, cb, cr, rgb;    
    int width, height, len;    
    
    Int32Rect roi;    
    YUVType yuvType;
    BinaryReader fin;
    WriteableBitmap bitmap;
    convertor convertToRGB;
    
    public delegate byte[] convertor();



    public YuvReader(Yuv.YUVType _type, int _width, int _height)
    {
      yuvType = _type;
      width   = _width;
      height  = _height;
      len     = width * height;

      y       = new byte[len];
      rgb     = new byte[len * 3];
      bitmap  = new WriteableBitmap(width/2, height/2, 96, 96, PixelFormats.Rgb24, null);
      roi     = new Int32Rect(0, 0, width/2, height/2);

      if (yuvType == YUVType.FOUR_TWO_ZERO)
      {
        convertToRGB = convertRGB24FromYUV420;
        cb     = new byte[len >> 2];
        cr     = new byte[len >> 2];
      }

      else if (yuvType == YUVType.FOUR_ZERO_ZERO)
      {
        convertToRGB = convertRGB24FromYUV400;
      }
    }



    public void open(string fileName)
    {
      fin = new BinaryReader(File.Open(fileName, FileMode.Open));
    }



    public void readFrame()
    {
      y   = fin.ReadBytes(len);
      if (yuvType == YUVType.FOUR_TWO_ZERO)
      {
        cb = fin.ReadBytes(len >> 2);
        cr = fin.ReadBytes(len >> 2);
      }

      else if (yuvType == YUVType.FOUR_ZERO_ZERO)
      {
         // to be continue...
      }                 
    }



    public WriteableBitmap getBitmap()
    {
      convertToRGB();
      bitmap.WritePixels(roi, rgb, width * 3, 0);
      return bitmap;
    }



    public byte[] convertRGB24FromYUV400()
    {
      int pos = 0;
      for (int j = 0; j < height; j++)
      {
        for (int i = 0; i < width; i++)
        {
          rgb[pos    ] = y[j * width + i];
          rgb[pos + 1] = y[j * width + i];
          rgb[pos + 2] = y[j * width + i];
          pos += 3;
        }
      }
      return rgb;
    }



    public byte[] convertRGB24FromYUV420()
    {
      int pos=0;
      for (int j = 0; j < height; j++)
      {
        for (int i = 0; i < width; i++, pos += 3)
        {
          int Y = y[j * width + i];
          int U = cb[(j >> 1) * (width >> 1) + (i >> 1)];
          int V = cr[(j >> 1) * (width >> 1) + (i >> 1)];
          rgb[pos    ]  = clip((76284 * (Y - 16) + 104595 * (V - 128)) >> 16);                      // r
          rgb[pos + 1]  = clip((76284 * (Y - 16) - 53281 * (V - 128) - 25625 * (U - 128)) >> 16);   // g
          rgb[pos + 2]  = clip((76284 * (Y - 16) + 132252 * (U - 128)) >> 16);                      // b                      
        }
      }
      return rgb;
    }



    private static byte clip(int data)
    {
      return (byte)((data < 0) ? 0 : ((data > 255) ? 255 : data));
    }
  }
}
